package com.demo.stock.redis;

import com.alibaba.fastjson.JSON;
import com.demo.stock.utils.SpringContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.params.SetParams;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/**
 * description: redis工具类
 *
 * @author zx
 * @version v1.0
 * @date 2019/10/19 14:28
 */
@Service("redisService")
public class RedisService {
    private static final Logger log = LoggerFactory.getLogger(RedisService.class);

    @Autowired
    private JedisConfiguration jedisConfiguration;

    /**
     * 查询剩余过期时间
     *
     * @param key redis key值
     * @return 过期时间，单位为秒
     */
    public long ttl(String key) {
        Jedis jedis = null;
        long result = 0L;
        try {
            jedis = getResource();
            return jedis.ttl(key);
        } catch (Exception e) {
            log.error("redis operate error ttl key:{}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 设置过期时间
     *
     * @param key     key值
     * @param seconds 过期时间，单位为秒
     * @return long
     */
    public long expire(String key, int seconds) {
        Jedis jedis = null;
        long result = 0L;
        try {
            jedis = getResource();
            if (seconds > 0) {
                return jedis.expire(key, seconds);
            }
        } catch (Exception e) {
            log.error("redis operate error ttl key:{}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 自增函数
     *
     * @param key        键
     * @param value      要自增的数据
     * @param expireTime 过期时间，单位为秒
     * @return long
     */
    public long incrBy(String key, long value, int expireTime) {
        Jedis jedis = null;
        long result = 0L;
        try {
            jedis = getResource();
            result = jedis.incrBy(key, value);
            // 只有第一次incr的时候才设置过期时间
            if (expireTime > 0 && result > value) {
                jedis.expire(key, expireTime);
            }
        } catch (Exception e) {
            log.error("redis operate error get key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return String
     */
    public String get(String key) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                value = jedis.get(key);
                value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
                log.info("redis operate info get key:{},value:{}", key, value);
            }
        } catch (Exception e) {
            log.error("redis operate error get key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return value;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public <T> T getObject(String key, Class<T> clazz) {
        T t = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                t = JSON.parseObject(jedis.get(key), clazz);
                log.info("redis operate info getObject key:{},value:{}", key, t);
            }
        } catch (Exception e) {
            log.error("redis operate error getObject key:{}", key, e);
        } finally {
            close(jedis);
        }
        return t;
    }

    /**
     * 设置缓存
     *
     * @param key         键
     * @param value       值
     * @param expireTimes 超时时间，0为不超时,单位为秒
     * @return String
     */
    public String set(String key, String value, int expireTimes) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.set(key, value);
            if (expireTimes > 0) {
                jedis.expire(key, expireTimes);
            }
            log.info("redis operate info set key:{},value:{}", key, value);
        } catch (Exception e) {
            log.error("redis operate error set key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 设置缓存
     *
     * @param key         键
     * @param value       值
     * @param expireTimes 超时时间，0为不超时,单位为秒
     * @return String
     */
    public String setex(String key, String value, int expireTimes) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.setex(key, expireTimes, value);
            log.info("redis operate info setex key:{},value:{}", key, value);
        } catch (Exception e) {
            log.error("redis operate error set key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 设置缓存
     *
     * @param key         键
     * @param object      值
     * @param expireTimes 超时时间，0为不超时,单位为秒
     * @return String
     */
    public String set(String key, Object object, int expireTimes) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            String value = JSON.toJSONString(object);
            result = jedis.set(key, value);
            if (expireTimes > 0) {
                jedis.expire(key, expireTimes);
            }
            log.info("redis operate info setObject key:{},value:{}", key, value);
        } catch (Exception e) {
            log.error("redis operate error setObject key:{}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取List缓存
     *
     * @param key 键
     * @return 值
     */
    public List<String> getList(String key) {
        List<String> value = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                value = jedis.lrange(key, 0, -1);
                log.info("redis operate info getList key:{},value:{}", key, value);
            }
        } catch (Exception e) {
            log.error("redis operate error getList key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return value;
    }

    /**
     * 获取List缓存
     *
     * @param key 键
     * @return List<T>
     */
    public <T> List<T> getObjectList(String key, Class<T> clazz) {
        List<T> result = null;
        Jedis jedis = null;
        List<String> list = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                list = jedis.lrange(key, 0, -1);
                log.info("redis operate info getObjectList key:{}", key);
            }
        } catch (Exception e) {
            log.error("redis operate error getObjectList key:{}", key, e);
        } finally {
            close(jedis);
        }
        try {
            if (null != list && list.size() != 0) {
                result = new ArrayList<>();
                for (String s : list) {
                    result.add(JSON.parseObject(s, clazz));
                }
            }
        } catch (Exception e) {
            log.error("convert string to object failed", e);
        }
        return result;
    }

    /**
     * 设置List缓存
     *
     * @param key         键
     * @param objectList  值
     * @param expireTimes 超时时间，0为不超时,单位为秒
     * @return long
     */
    public long rpush(String key, List<Object> objectList, int expireTimes) {
        List<String> list = new ArrayList<>();
        for (Object o : objectList) {
            list.add(JSON.toJSONString(o));
        }
        String[] array = new String[list.size()];
        list.toArray(array);

        long result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.rpush(key, array);
            if (expireTimes > 0) {
                jedis.expire(key, expireTimes);
            }
            log.info("redis operate info setObjectList key:{}", key);
        } catch (Exception e) {
            log.error("redis operate error setObjectList key:{}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public Set<String> smembers(String key) {
        Set<String> value = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                value = jedis.smembers(key);
                log.info("redis operate info getSet key:{},value:{}", key, value);
            }
        } catch (Exception e) {
            log.error("redis operate error getSet key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return value;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public Map<String, String> hgetAll(String key) {
        Map<String, String> value = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                value = jedis.hgetAll(key);
                log.info("redis operate info getMap key:{},value:{}", key, value);
            }
        } catch (Exception e) {
            log.error("redis operate error getMap key:{},value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return value;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public <T> Map<String, T> hgetAll(String key, Class<T> clazz) {
        Map<String, T> result = new HashMap<>(128);
        Jedis jedis = null;
        Map<String, String> map = new HashMap<>(128);
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                map = jedis.hgetAll(key);
                log.info("redis operate info getObjectMap key:{}", key);
            }
        } catch (Exception e) {
            log.error("redis operate error getObjectMap key:{}", key, e);
        } finally {
            close(jedis);
        }
        for (Map.Entry<String, String> e : map.entrySet()) {
            result.put(e.getKey(), JSON.parseObject(e.getValue(), clazz));
        }
        return result;
    }

    /**
     * 获取hash中指定的key value
     *
     * @param key key
     * @param hashKey hashKey
     * @return String
     */
    public String hget(String key, String hashKey) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            value = jedis.hget(key, hashKey);
        } catch (Exception e) {
            log.error("redis operate error hget key:{}, hashKey:{}", key, hashKey, e);
        } finally {
            close(jedis);
        }
        return value;
    }

    /**
     * 设置Map缓存
     *
     * @param key         键
     * @param value       值
     * @param expireTimes 超时时间，0为不超时,单位为秒
     * @return String
     */
    public <T> String hmset(String key, Map<String, T> value, int expireTimes) {
        String result = null;
        Jedis jedis = null;
        Map<String, String> map = new HashMap<>(128);
        for (Map.Entry<String, T> e : value.entrySet()) {
            map.put(e.getKey(), JSON.toJSONString(e.getValue()));
        }
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                jedis.del(key);
            }
            result = jedis.hmset(key, map);
            if (expireTimes > 0) {
                jedis.expire(key, expireTimes);
            }
            log.info("redis operate info setObjectMap key:{}", key);
        } catch (Exception e) {
            log.error("redis operate error setObjectMap key:{},", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 设置Map缓存中的值
     *
     * @param key     键
     * @param hashKey hashKey
     * @param value   值
     * @return Long
     */
    public Long hset(String key, String hashKey, String value) {
        Long result = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.hset(key, hashKey, value);
        } catch (Exception e) {
            log.error("redis operate hset error, key:{}, hashKey:{}, value:{}", key, hashKey, value, e);
        } finally {
            close(jedis);
        }
        return result;
    }


    /**
     * 移除Map缓存中的值
     *
     * @param key    键
     * @param mapKey 键
     * @return long
     */
    public long hdel(String key, String mapKey) {
        long result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.hdel(key, mapKey);
            log.info("redis operate info mapRemove key:{},mapKey:{}", key, mapKey);
        } catch (Exception e) {
            log.error("redis operate error mapRemove key:{},mapKey:{}", key, mapKey, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 判断Map缓存中的Key是否存在
     *
     * @param key    键
     * @param mapKey 键
     * @return boolean
     */
    public boolean hexists(String key, String mapKey) {
        boolean result = false;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.hexists(key, mapKey);
            log.info("redis operate info mapExists {}  {}", key, mapKey);
        } catch (Exception e) {
            log.error("redis operate error mapExists {}  {}", key, mapKey, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 往zset中添加数据，如果已存在，则覆盖值
     *
     * @param key 键
     * @param score 分数，根据该值排名，有点类似hashKey
     * @param member 成员，有点类似
     * @return long
     */
    public long zadd(String key, double score, String member) {
        long result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.zadd(key, score, member);

            log.info("redis operate info exists {}", key);
        } catch (Exception e) {
            log.error("redis operate error exists {}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    public double zscore(String key, String member) {
        double result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.zscore(key, member);
            log.info("redis operate info exists {}", key);
        } catch (Exception e) {
            log.error("redis operate error exists {}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取指定排名内的数据，根据score从高到低排名,排名从0开始
     *
     * @param key redis键
     * @return Set<String>
     */
    public Set<String> zrevrange(String key) {
        Set<String> set = new HashSet<>();
        Jedis jedis = null;
        try {
            jedis = getResource();
            set = jedis.zrevrange(key, 0, 100);
            log.info("redis operate info exists {}", key);
        } catch (Exception e) {
            log.error("redis operate error exists {}", key, e);
        } finally {
            close(jedis);
        }
        return set;
    }

    public double zincrby(String key, double score, String member) {
        double result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.zincrby(key, score, member);
            log.info("redis operate info exists {}", key);
        } catch (Exception e) {
            log.error("redis operate error exists {}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 删除缓存
     *
     * @param key 键
     * @return long
     */
    public long del(String key) {
        long result = 0;
        Jedis jedis = null;
        try {
            jedis = getResource();
            if (jedis.exists(key)) {
                result = jedis.del(key);
                log.info("redis operate info del {}", key);
            }
        } catch (Exception e) {
            log.error("redis operate error del {}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return boolean
     */
    public boolean exists(String key) {
        boolean result = false;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = jedis.exists(key);
            log.info("redis operate info exists {}", key);
        } catch (Exception e) {
            log.error("redis operate error exists {}", key, e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 设置锁
     *
     * @param key           键
     * @param value         值
     * @param expireSeconds 过期时间
     * @return boolean
     */
    public boolean setNxEx(String key, String value, int expireSeconds) {
        Jedis jedis = null;
        try {
            jedis = getResource();
            String flag = jedis.set(key, value, SetParams.setParams().nx().ex(expireSeconds));
            return "ok".equalsIgnoreCase(flag);
        } catch (Exception e) {
            log.error("redis operate error setNxEx, key:{}, value:{}", key, value, e);
        } finally {
            close(jedis);
        }
        return false;
    }

    /**
     * 执行lua脚本
     *
     * @param script lua脚本
     * @param keys   redis key
     * @param values 参数
     * @return String
     */
    public String eval(String script, List<String> keys, List<String> values) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getResource();
            result = (String) jedis.eval(script, keys, values);
        } catch (Exception e) {
            log.error("redis eval error, script:{}, keys:{}, values:{}", script, JSON.toJSONString(keys), JSON.toJSONString(values), e);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取连接
     */
    public Jedis getResource() throws JedisException {
        Jedis jedis = null;
        try {
            jedis = jedisConfiguration.redisPoolFactory().getResource();
        } catch (JedisException e) {
            log.error("redis operate error getResource.", e);
            throw e;
        }
        return jedis;
    }

    /**
     * 释放资源
     *
     * @param jedis 连接
     */
    public void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}
